WOLF ROS2 publishers
WOLF ROS2 publishers are the procedures reponsible for generating/broadcasting information derived from the WOLF tree to the ROS2 ecosystem.
wolf::InterfaceBase
The abstract class wolf::InterfaceBase provides common functionality for both subscribers and publishers:
Stores the node pointer, giving access to the logger and the timer with the corresponding getters.
Stores the derived type (string).
Stores the topic (string).
wolf::PublisherBase
All WOLF publishers must inherit from the base class wolf::PublisherBase defined in the wolf_ros2_node package.
class PublisherBase : public InterfaceBase
{
protected:
ProblemConstPtr problem_;
double period_;
rclcpp::Time first_publish_time_;
long unsigned int last_n_period_;
std::thread pub_thread_;
// PROFILING
ProfilingUnit profiling_;
public:
PublisherBase(rclcpp::Node::SharedPtr _node, ProblemConstPtr _problem, const YAML::Node &_params)
: InterfaceBase(_node, _params),
problem_(_problem),
period_(_params["period"].as<double>()),
first_publish_time_(rclcpp::Time(0)),
last_n_period_(0),
profiling_(_params["period"].as<double>()){};
virtual ~PublisherBase(){};
virtual void run() final;
virtual void stop() final;
virtual void loop() final;
virtual void publish() final;
virtual void publishDerived() = 0;
virtual bool ready();
void printProfiling(std::ostream &stream = std::cout) const;
};
This abstract class provides common functionality for all WOLF publishers:
WOLF problem pointer is stored in an attribute. The derived publisher can have access to it to publish the desired information.
Own trhead as an attribute along with related methods
run(),stop(),loop()andready()which can be overritten in the derived class if needed.Profiling: A ProfilingUnit object is stored as an attribute . Profiling is automatically performed in
publish()that calls the pure virtual methodpublishDerived(), which has to be implemented in the derived class.
Creating a publisher
To implement a new publisher, you need to derive from the wolf::PublisherBase class.
Definition
Analogously to other factory-based nodes in WOLF, the constructor should have a standard API
and we provide the macro WOLF_PUBLISHER_CREATE to automatically implement the creator:
PublisherDerived(rclcpp::Node::SharedPtr _node,
ProblemConstPtr _problem,
const YAML::Node& _params);
WOLF_PUBLISHER_CREATE(PublisherDerived);
Also, add a ROS2 publisher attribute in the class definition. For example:
rclcpp::Publisher<std_msgs::msg::Float64MultiArray>::SharedPtr pub_state_block_;
Implementation
The constructor of the derived publisher should include:
Getting desired parameters.
Creation of the publisher providing the topic stored in
InterfaceBaseviagetTopic().
Register the creator in the corresponding factory using the macro
WOLF_REGISTER_PUBLISHER.Implement the publishDerived() method that should get the desired information from the WOLF problem and publish it using the publisher attribute created in the constructor.
The following is an example of a derived publisher:
namespace wolf
{
PublisherStateBlock::PublisherStateBlock(rclcpp::Node::SharedPtr _node,
ProblemConstPtr _problem,
const YAML::Node& _params)
: PublisherBase(_node, _problem, _params), msg_init_(false)
{
sensor_ = _problem->findSensor(_params["sensor"].as<std::string>());
assert(sensor_);
key_ = _params["key"].as<char>();
pub_state_block_ = _node->create_publisher<std_msgs::msg::Float64MultiArray>(getTopic(), 1);
}
void PublisherStateBlock::publishDerived()
{
WOLF_WARN_COND(not sensor_->getStateBlock(key_), "StateBlock not found")
if (not sensor_->getStateBlock(key_)) return;
Eigen::VectorXd state_vec = sensor_->getStateBlock(key_)->getState();
if (not msg_init_)
{
std_msgs::msg::MultiArrayDimension dim;
dim.label = sensor_->getName() + "/" + std::to_string(key_);
dim.size = state_vec.size();
dim.stride = state_vec.size();
state_msg_.layout.dim.push_back(dim);
state_msg_.layout.data_offset = 0;
state_msg_.data.resize(state_vec.size());
msg_init_ = true;
}
Eigen::Map<Eigen::VectorXd> msg_map(state_msg_.data.data(), state_vec.size());
msg_map = state_vec;
pub_state_block_->publish(state_msg_);
}
WOLF_REGISTER_PUBLISHER(PublisherStateBlock)
} // namespace wolf
Specification
You will have to create the corresponding schema file specifying the YAML parameters. The following is an example of a derived publisher schema:
follow: PublisherBase.schema
sensor:
_type: std::string
_mandatory: true
_doc: "The sensor name from which the state block value should be published"
key:
_type: char
_mandatory: true
_doc: "The key of the state block that should be published"
Finally, remember to add the new subscriber to the CMakeLists.txt of the correponding package.